package communication {
	import com.hurlant.crypto.Crypto;
	import com.hurlant.crypto.prng.Random;
	import com.hurlant.crypto.rsa.RSAKey;
	import com.hurlant.crypto.symmetric.ICipher;
	import com.hurlant.crypto.symmetric.IPad;
	import com.hurlant.crypto.symmetric.IVMode;
	import com.hurlant.crypto.symmetric.PKCS5;
	import com.netease.protobuf.Message;
	
	import flash.utils.ByteArray;
	
	import crypto.MahjongPEM;
	import crypto.RSAKeyGenerate;
	
	import protobuf.CLIENT_SESSION_CERT;
	import protobuf.LG_PKG_LOGIN_REQ;
	
	public final class CertManager {

		private static var _instance:CertManager;

		public static function getInstance():CertManager {
			if (_instance == null) {
				_instance=new CertManager();
				_instance.initRSAKey();
			}
			return _instance;
		}

		public function initRSAKey():void {
			var loginKeyBytes:ByteArray=new publicKey_pem;
			var loginKeyStr:String=loginKeyBytes.readUTFBytes(loginKeyBytes.length);
			loginRSA=MahjongPEM.readRSAPublicKey(loginKeyStr);
			clientRSA=RSAKey.generate(CLIENT_RSA_BITS, CLIENT_RSA_EXP);
			clientRSAPublic=RSAKeyGenerate.getPublicKey(clientRSA);
		}

		[Embed(source="../../embeds/public_key.pem", mimeType="application/octet-stream")]
		private var publicKey_pem:Class;
		private var loginRSA:RSAKey;
		private var clientRSA:RSAKey;
		private var clientRSAPublic:ByteArray;

		private var clientSessionCert:CLIENT_SESSION_CERT;
		private static const CLIENT_RSA_EXP:String="10001";
		private static const CLIENT_RSA_BITS:int=512;

		public function getClientSessionCert():CLIENT_SESSION_CERT {
			return clientSessionCert;
		}

		/**
		 *  UTF-8 字符串转换成字节流
		 **/
		public function convertStringToByteArray(string:String):ByteArray {
			var bytes:ByteArray;
			if (string) {
				bytes=new ByteArray();
				bytes.writeUTFBytes(string); // writeUTFBytes(value:String) 将 UTF-8 字符串写入字节流;
			}
			return bytes;
		}
		
		/**
		 * 字节流转换为 UTF-8 字符串
		 **/
		public function convertByteArrayToString(byteArray:ByteArray):String
		{
			if(byteArray == null) return "";
			byteArray.position = 0;
			return byteArray.readUTFBytes(byteArray.length);
		}

		public function getClientRSAPublicKey():ByteArray {
			return clientRSAPublic;
		}

		public function parseAuthRequest(authInfo:Message, method:int):LG_PKG_LOGIN_REQ {
			logger("encrypt Auth info...");
			var key:ByteArray=genAESKey(128);
			var data:ByteArray=new ByteArray();
			authInfo.writeTo(data);
			var sizeBeforeEncrypt:int=data.length;
			logger("Auth info before encrypt: " + data.length);
			var name:String="aes-cbc";

			var pad:IPad=new PKCS5;
			var mode:ICipher=Crypto.getCipher(name, key, pad);
			pad.setBlockSize(mode.getBlockSize());
			mode.encrypt(data);

			logger("Auth info after encrypt: " + data.length);
			var ivmode:IVMode=mode as IVMode;
			var iv:ByteArray=ivmode.IV;

			var request:LG_PKG_LOGIN_REQ=new LG_PKG_LOGIN_REQ();
			request.method=method;
			request.encryptedAuthInfo=data;
			request.ivAuthInfo=iv;
			request.encryptedKey=encryptAESKey(key);
			request.authInfoSize=sizeBeforeEncrypt;
			return request;
		}

		private function encryptAESKey(key:ByteArray):ByteArray {
			logger("encrypt AES Key...");
			var encryptKey:ByteArray=new ByteArray();
			loginRSA.encrypt(key, encryptKey, key.length);
			return encryptKey;
		}

		private function genAESKey(v:int):ByteArray {
			var r:Random=new Random;
			var b:ByteArray=new ByteArray
			r.nextBytes(b, v / 8);
			return b;
		}

		public function initClientSessionCert(clientSessionCert:CLIENT_SESSION_CERT):void {
			this.clientSessionCert=clientSessionCert;
			clientSessionCert.publicKey=clientRSAPublic;
		}

		public function sign(src:ByteArray, out:ByteArray, length:int):void {
			clientRSA.sign(src, out, length);
		}
	}
}